import { exec, spawn } from "child_process";
import { existsSync } from "fs";
import { cpDir, cpFile, IIgnoreOption } from "../utils/FileUtil";
import { compressDir } from "../tinypng/TinyHtml";
import { compressJsAsync } from "../utils/JsCompress";
import { getRoot } from "../data/Config";
import { EventEmitter } from "events";
import { IPublishVo, IProjectVo } from "../data/Config";
var path = require('path');
const iconv = require("iconv-lite");

export abstract class PublishBase extends EventEmitter {
    abstract get projDir(): string;     //项目目录名字
    protected _publishVo: IPublishVo;
    protected mProjectInfo: IProjectVo;
    protected _version: string;
    protected _checkRes: boolean;
    private _logFunc: Function;
    protected _complete: Function;
    protected _startTime: number;
    protected mOutPath: string;          //输出目录
    protected mProjPath: string;         //导出项目目录
    protected mTempProjPath: string;     //模板项目地址
    protected mToolPath: string;     //工具地址
    protected mFromPath: string;          //项目来源目录
    private _config: any;
    init($publishVo: IPublishVo, projectInfo: IProjectVo) {
        for (let i in $publishVo) {
            if (typeof $publishVo[i] == "string") {
                $publishVo[i] = $publishVo[i].replace(new RegExp("${project}", "g"), projectInfo.dir);
            }

            if (typeof $publishVo[i] == "object") {
                for (let a in $publishVo[i]) {
                    if (typeof $publishVo[i][a] == "string") {
                        $publishVo[i][a] = $publishVo[i][a].replace(new RegExp("${project}", "g"), projectInfo.dir);
                    }
                }
            }
        }
        this._publishVo = $publishVo;
        this.mProjectInfo = projectInfo;
        this.mFromPath = $publishVo.fromPath;
        this.mOutPath = this._publishVo.toPath;
        this.mProjPath = path.join(this.mOutPath, this.projDir);
        this.mTempProjPath = path.join(__dirname, '../../assets/', 'templates', this.projDir);
        this.mToolPath = path.join(__dirname, '../../assets/', 'tools');
    }

    packGame(version: string, checkRes: boolean, logFunc: (log: string) => void, complete: Function) {
        this._version = version;
        this._startTime = Date.now();
        this._checkRes = checkRes;
        this._logFunc = logFunc;
        this._complete = complete;
        setTimeout(() => {
            this.createProject()
        }, 1);
    }

    abstract createProject();

    abstract doPack();

    showPreView(logFunc: Function) {
        logFunc && logFunc("本渠道不支持预览命令");
    }

    packOver() {
        let totalTime = Date.now() - this._startTime;
        this.log(`打包结束! 总用时 ${totalTime}ms------` + (new Date()).toString());
        this._complete && this._complete(true);
    }

    log(msg: string) {
        this._logFunc && this._logFunc(msg);
    }

    debug(msg: string) {
        this.emit("debug", msg);
    }

    update(data: any) {
        this.emit("update", data);
    }

    getConfigName() {
        if (this._publishVo.plat) {
            return `config_${this._publishVo.plat}.json`;
        }
        return 'config.json';
    }

    get config() {
        if (this._publishVo.config) {
            return this._publishVo.config.replace(/\$\{VER\}/g, this._version);
        }
        return "";
    }

    async exec(cmd: string, option?: any) {
        return new Promise((resolve, reject) => {
            var t = Date.now();
            console.log("开始执行:" + cmd);
            exec(cmd, option, (err, stdout, stderr) => {
                if (err) {
                    this.log("执行失败:" + cmd + "\nError:" + err.message);
                    resolve(null);
                    return;
                }
                this.printMsg("执行成功:" + cmd + "         耗时(" + (Date.now() - t) + ")毫秒");
                resolve(stdout);
            })
        });
    }

    spawn(cmd: string, args: string[], options?: any, updateFunc?: (log: string) => void) {
        let cp = spawn(cmd, args, options);
        cp.stdout.on('data', (data) => {
            console.log(`${data}`);
            updateFunc && updateFunc(data.toString());
            // 输出pid，macos要用: macos无法kill进程树，也无法执行命令获取3000端口pid(没有查询权限)，导致无法kill这个进程
            console.log('vv_qrcode_pid:' + cp.pid);
        });

        cp.stderr.on('data', (data) => {
            console.log(`stderr: ${data}`);
            console.log(`stderr(iconv): ${iconv.decode(data, 'gbk')}`);
            // reject();
        });

        cp.on('close', (code) => {
            console.log(`子进程退出码：${code}`);
            // resolve();
        });
        return cp;
    }

    async compressJs(jsList: string[], binPath: string, toPath: string) {
        for (let i = 0; i < jsList.length; i++) {
            let jsUri = jsList[i];
            let outputPath = path.join(toPath, jsUri);
            let needCompress: boolean = true;
            if(!this._publishVo.unCompessJs){
                if (jsUri.indexOf('libs/min/') < 0 && jsUri.indexOf('libs/') >= 0) {
                    let jsLibUri = jsUri.replace('libs/', 'libs/min/');
                    if (existsSync(path.join(binPath, jsLibUri))) {
                        jsUri = jsLibUri;
                        needCompress = false;
                    }
                }
            }else{
                needCompress=false;
            }
            let jsPath = path.join(binPath, jsUri);
            let existJs = existsSync(jsPath);
            if (!existJs) {
                console.warn('没有这个js文件, 跳过', jsUri);
                continue;
            }
            if (!needCompress) {
                cpFile(jsPath, outputPath);
                continue;
            }
            await compressJsAsync(jsPath, outputPath);
        }
    }

    async copyDir(dirPath: string, toDirPath: string, hash: boolean = false, ignores?: IIgnoreOption) {
        return new Promise((resolve, reject) => {
            cpDir(dirPath, toDirPath, () => {
                resolve(null);
            }, hash, ignores);
        })
    }

    async compressImg(dirPath: string, excludeHash?: any) {
        return new Promise((resolve, reject) => {
            compressDir(dirPath, () => {
                resolve(null);
            }, this.log.bind(this), excludeHash);
        })
    }

    printMsg(msg: string) {
        console.log(msg);
    }

    /** 压缩js库 */
    public compressLibs() {
        console.log('压缩js库', this._publishVo);
        // compressJs()
    }
}